<?php
/**
 *
 * JSAPI支付实现类
 * 该类实现了从微信公众平台获取code、通过code获取openid和access_token、
 * 生成jsapi支付js接口所需的参数、生成获取共享收货地址所需的参数
 *
 * 该类是微信支付提供的样例程序，商户可根据自己的需求修改，或者使用lib中的api自行开发
 *
 *
 * 网页授权接口微信服务器返回的数据，返回样例如下
 * {
 *  "access_token":"ACCESS_TOKEN",
 *  "expires_in":7200,
 *  "refresh_token":"REFRESH_TOKEN",
 *  "openid":"OPENID",
 *  "scope":"SCOPE",
 *  "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
 * }
 * 其中access_token可用于获取共享收货地址
 * openid是微信支付jsapi支付接口必须的参数
 * @var array
 *
 */

namespace Think\Wxpay\Driver;

use Think\Wxpay\Driver;

class JsApiPay
{
    /**
     *
     * 网页授权接口微信服务器返回的数据，返回样例如下
     * {
     *  "access_token":"ACCESS_TOKEN",
     *  "expires_in":7200,
     *  "refresh_token":"REFRESH_TOKEN",
     *  "openid":"OPENID",
     *  "scope":"SCOPE",
     *  "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
     * }
     * 其中access_token可用于获取共享收货地址
     * openid是微信支付jsapi支付接口必须的参数
     * @var array
     */
    public $data = null;
    /**
     *
     * 通过跳转获取用户的openid，跳转流程如下：
     * 1、设置自己需要调回的url及其其他参数，跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize
     * 2、微信服务处理完成之后会跳转回用户redirect_uri地址，此时会带上一些参数，如：code
     *
     * @return 用户的openid
     */
    public function GetOpenid($code)
    {


        $info = $this->__getOpenid();

        if($info && $info['openid'] && $info['access_token']){

            $this->data = $info;

            return $this->data['openid'];
        }

        $openid = $this->getOpenidFromMp($code);

        if(is_array($openid))
            return ['code' => 100,'msg' => $info['errmsg'],'data' => $openid];

        $this->__storeOpenid();

        return $openid;
    }

    /*
     * 获取微信用户信息
     *
     */
    public function GetUserInfo($code) {

        //检测access_token
        $openid = $this->GetOpenid($code);

        $path = CACHE_PATH.'WeChat/userinfo';

        if(!$this->__checkAccessToken()){

            //如果access_token过去,则重新获取

            $file = $path.'/'.$openid.'.json';

            @unlink($file);

            $openid = $this->GetOpenid($code);


        }

        if(isset($openid['code']) && $openid['code'] == 100)
            return $openid;


        if (!is_dir($path))
            mkdir($path,0777,true);

        $cache_file = $path.'/'.$openid.'.json';

        if (is_file($cache_file) && is_readable($cache_file) && filemtime($cache_file) > time() - 7000) {

            $data = json_decode(file_get_contents($cache_file),true);

            return ['code' => 200,'data' => ['userinfo' => $data] ];

        }

        $url = $this->__CreateOauthUrlForUser();
        $res = $this->__request($url);


        $res = json_decode($res);

        $res = json_decode(json_encode($res), true);



        if(isset($res['errcode']))
            return ['code' => 300, 'msg' => $res['errmsg'],'error_info' => $res];

        //缓存用户信息
        file_put_contents($cache_file, json_encode($res));

        return [ 'code' => 200, 'data' => ['userinfo' => $res ] ];
    }

    private function __getOpenid() {

        $ip = get_client_ip();

        $path = CACHE_PATH.'WeChat/openid/';

        $cache_file = $path.$ip.'.json';

        if (is_file($cache_file) && is_readable($cache_file) && filemtime($cache_file) > time() - 7000) {

            $data = json_decode(file_get_contents($cache_file),true);

            if($data['openid'] && $data['access_token'])
                return $data;
        }

        @unlink($cache_file);

        return null;
    }

    private function __storeOpenid() {
        $ip = get_client_ip();

        $path = CACHE_PATH.'WeChat/openid/';

        if(!is_dir($path))
            mkdir($path,0777,true);

        $file = $path.$ip.'.json';

        file_put_contents($file,json_encode($this->data));

    }

    /**
     *
     * 获取jsapi支付的参数
     * @param array $UnifiedOrderResult 统一支付接口返回的数据
     * @throws Exception
     *
     * @return json数据，可直接填入js函数作为参数
     */
    public function GetJsApiParameters($UnifiedOrderResult)
    {

        if(!array_key_exists("appid", $UnifiedOrderResult)
            || !array_key_exists("prepay_id", $UnifiedOrderResult)
            || $UnifiedOrderResult['prepay_id'] == "")
        {
            throw new WxPayException("参数错误");
        }
        $jsapi = new WxPayJsApiPay();
        $jsapi->SetAppid($UnifiedOrderResult["appid"]);
        $timeStamp = time();
        $jsapi->SetTimeStamp("$timeStamp");
        $jsapi->SetNonceStr(WxPayApi::getNonceStr());
        $jsapi->SetPackage("prepay_id=" . $UnifiedOrderResult['prepay_id']);
        $jsapi->SetSignType("MD5");
        $jsapi->SetPaySign($jsapi->MakeSign());
        //$parameters = json_encode($jsapi->GetValues());
        $parameters = $jsapi->GetValues();
        return $parameters;
    }

    /**
     *
     * 通过code从工作平台获取openid机器access_token
     * @param string $code 微信跳转回来带上的code
     *
     * @return openid
     */
    public function GetOpenidFromMp($code, $curl_timeout)
    {
        $url = $this->__CreateOauthUrlForOpenid($code);
        $res = $this->__request($url, $curl_timeout);

        //file_put_contents(RUNTIME_PATH.'logs.log',$res);

        //取出openid
        $data = json_decode($res);

        $data = json_decode(json_encode($data), true);

        if(isset($data['errcode']))
            return $data;

        //file_put_contents(RUNTIME_PATH.'type.log',gettype($data));

        $this->data = $data;

        return $data['openid'];
    }

    private function __checkAccessToken() {
        $url = $this->__CreateOauthUrlForAccessToken();
        $res = $this->__request($url);
        $data = json_decode($res,true);
        return $data['errcode'] == 0;
    }

    private function __request($url, $curl_timeout = 30) {
        //初始化curl
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $curl_timeout);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,FALSE);
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

//        if(curl_errno($ch))
//        {
//            echo 'Curl error: ' . curl_error($ch);
//            exit;
//        }

        //运行curl，结果以jason形式返回
        $res = curl_exec($ch);
        curl_close($ch);

        return $res;
    }

    /**
     *
     * 拼接签名字符串
     * @param array $urlObj
     *
     * @return 返回已经拼接好的字符串
     */
    private function ToUrlParams($urlObj)
    {
        $buff = "";
        foreach ($urlObj as $k => $v)
        {
            if($k != "sign"){
                $buff .= $k . "=" . $v . "&";
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }

    /**
     *
     * 获取地址js参数
     *
     * @return 获取共享收货地址js函数需要的参数，json格式可以直接做参数使用
     */
    public function GetEditAddressParameters()
    {
        $getData = $this->data;
        $data = array();
        $data["appid"] = C('WeChat_WeChat.APPID');
        $data["url"] = "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
        $time = time();
        $data["timestamp"] = "$time";
        $data["noncestr"] = "1234568";
        $data["accesstoken"] = $getData["access_token"];
        ksort($data);
        $params = $this->ToUrlParams($data);
        $addrSign = sha1($params);

        $afterData = array(
            "addrSign" => $addrSign,
            "signType" => "sha1",
            "scope" => "jsapi_address",
            "appId" => C('WeChat_WeChat.APPID'),
            "timeStamp" => $data["timestamp"],
            "nonceStr" => $data["noncestr"]
        );
        $parameters = json_encode($afterData);
        return $parameters;
    }

    /**
     *
     * 构造获取code的url连接
     * @param string $redirectUrl 微信服务器回跳的url，需要url编码
     *
     * @return 返回构造好的url
     */
    private function __CreateOauthUrlForCode($redirectUrl)
    {
        $urlObj["appid"] = C('WeChat_WeChat.APPID');
        $urlObj["redirect_uri"] = "$redirectUrl";
        $urlObj["response_type"] = "code";
        // $urlObj["scope"] = "snsapi_base";
        $urlObj["scope"] = "snsapi_userinfo";
        $urlObj["state"] = "STATE"."#wechat_redirect";
        $bizString = $this->ToUrlParams($urlObj);
        return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString;
    }

    private function __CreateOauthUrlForUser() {
        $urlObj['access_token'] = $this->data['access_token'];
        $urlObj['openid'] = $this->data['openid'];
        $urlObj['lang'] = 'zh_CN';
        $bizString = $this->ToUrlParams($urlObj);
        return "https://api.weixin.qq.com/sns/userinfo?".$bizString;
    }

    private function __CreateOauthUrlForAccessToken() {
        $urlObj['access_token'] = $this->data['access_token'];
        $urlObj['openid'] = $this->data['openid'];
        $bizString = $this->ToUrlParams($urlObj);
        return "https://api.weixin.qq.com/sns/auth?".$bizString;
    }

    /**
     *
     * 构造获取open和access_toke的url地址
     * @param string $code，微信跳转带回的code
     *
     * @return 请求的url
     */
    private function __CreateOauthUrlForOpenid($code)
    {
        $urlObj["appid"] = C('WeChat_WeChat.APPID');
        $urlObj["secret"] = C('WeChat_WeChat.APPSECRET');
        $urlObj["code"] = $code;
        $urlObj["grant_type"] = "authorization_code";
        $bizString = $this->ToUrlParams($urlObj);
        return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString;
    }
}